//: C25:ShapeFactory2.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 1999
// Copyright notice in Copyright.txt
// Polymorphic factory methods
#include "../purge.h"
#include <iostream>
#include <string>
#include <exception>
#include <vector>
#include <map>
using namespace std;

class Shape
{
public:
    virtual void draw() = 0;
    virtual void erase() = 0;
    virtual ~Shape() {}
};

class ShapeFactory
{
    virtual Shape* create() = 0;
    static map<string, ShapeFactory*> factories;
public:
    virtual ~ShapeFactory() {}
    friend class ShapeFactoryInizializer;
    class BadShapeCreation : public exception
    {
        string reason;
    public:
        BadShapeCreation(string type) {
            reason = "Cannot create type " + type;
        }
        const char *what() const {
            return reason.c_str();
        }
    };
    static Shape*
    createShape(string id) throw(BadShapeCreation) {
        if (factories.find(id) != factories.end())
            return factories[id]->create();
        else
            throw BadShapeCreation(id);
    }
};

// Define the static object:
map<string, ShapeFactory*>
ShapeFactory::factories;

class Circle : public Shape
{
    Circle() {} // Private constructor
public:
    void draw() {
        cout << "Circle::draw\n";
    }
    void erase() {
        cout << "Circle::erase\n";
    }
    ~Circle() {
        cout << "Circle::~Circle\n";
    }
    class Factory;
    friend class Factory;
    class Factory : public ShapeFactory
    {
    public:
        Shape* create() {
            return new Circle;
        }
    };
};

class Square : public Shape
{
    Square() {}
public:
    void draw() {
        cout << "Square::draw\n";
    }
    void erase() {
        cout << "Square::erase\n";
    }
    ~Square() {
        cout << "Square::~Square\n";
    }
    class Factory;
    friend class Factory;
    class Factory : public ShapeFactory
    {
    public:
        Shape* create() {
            return new Square;
        }
    };
};

// Singleton to initialize the ShapeFactory:
class ShapeFactoryInizializer
{
    static ShapeFactoryInizializer si;
    ShapeFactoryInizializer() {
        ShapeFactory::factories["Circle"] =
            new Circle::Factory;
        ShapeFactory::factories["Square"] =
            new Square::Factory;
    }
};

// Static member definition:
ShapeFactoryInizializer
ShapeFactoryInizializer::si;

char* shlist[] = { "Circle", "Square", "Square",
                   "Circle", "Circle", "Circle", "Square", ""
                 };

int main()
{
    vector<Shape*> shapes;
    try {
        for (char** cp = shlist; **cp; cp++)
            shapes.push_back(
                ShapeFactory::createShape(*cp));
    } catch (ShapeFactory::BadShapeCreation e) {
        cout << e.what() << endl;
        return 1;
    }
    for (int i = 0; i < shapes.size(); i++) {
        shapes[i]->draw();
        shapes[i]->erase();
    }
    purge(shapes);
} ///:~
